生成 AI Slack ボットにモデル切り替え機能を実装してみた
こんにちは、森田です。
生成AIアプリケーションを利用する際に、モデルは1つだけではなく、多くのモデルから選択できると嬉しいですよね。
このようなケースでは、アプリケーション側でモデルを入力するインタフェースを用意する必要があります。
例えば、よくあるWEBアプリですと、以下のようにデフォルトのモデルが設定されており、モデルを変更したい時だけ設定値を変更するような使い方ができます。
引用: https://classmethod.jp/services/generative-ai/ai-starter/
一方で、チャットボットでは、上記のようなモデルの切り替えをしたいとなった時には、少し工夫が必要です。
本記事では、Slackでのチャットボットにおけるモデル切り替え機能を実装してみたいと思います。
前提
チャットボットのベースは以下のリポジトリのコードを使用します。
モデルの切り替えについて
今回は、スラッシュコマンドからモーダルを開き、モデルの切り替えができるようにしてみます。
また、せっかくなので、プロンプトも設定できるようにしていきます。
やってみた
全体のコードは以下のリンクへ格納しています。
重要な項目だけピックアップして紹介します。
DynamoDB の作成
ユーザのモデルID、プロンプト情報の記録を行うため、DynamoDBで新規にテーブルを作成します。
以下のように、UserIdをキーとするシンプルなテーブルを作成します。
UserTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: UserId
AttributeType: S
KeySchema:
- AttributeName: UserId
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
Lazy Listener の利用
今回実装する処理については、DBの書き込み・読み込みを行うため、タイムアウトエラーを考慮して、Lazy Listener を利用します。
スラッシュコマンドの作成
スラッシュコマンド実行時にトリガーされる処理を作成します。
from slack_app import app, model_id_list
from modal.edit_view import view
from lib.user_db import get_item
def edit(body, client, logger, say):
user_id = body["user_id"]
# ユーザ情報の取得
user_data = get_item(user_id)
client.views_open(
trigger_id=body["trigger_id"],
view=view(user_data, model_id_list)
)
def ack_only(ack):
ack()
app.command("/edit")(
ack=ack_only,
lazy=[edit]
)
上記のコードでは、DynamoDBからユーザ情報の取得を行い、ユーザ情報に基づいて、モーダルの表示を行います。
def view(user_data, model_id_list):
options = [
{
"text": {
"type": "plain_text",
"text": model["ModelId"],
"emoji": True
},
"value": model["ModelId"]
} for model in model_id_list
]
return {
"type": "modal",
"callback_id": "edit_view",
"title": {"type": "plain_text", "text":"ユーザ情報変更"},
"submit": {"type": "plain_text", "text":"送信"},
"blocks": [
{
"type": "input",
"block_id": "model_id",
"element": {
"type": "static_select",
"placeholder": {
"type": "plain_text",
"text": user_data["ModelId"],
"emoji": True
},
"options": options,
"action_id": "static_select-action"
},
"label": {
"type": "plain_text",
"text": "モデルID",
"emoji": True
}
},
{
"type": "input",
"block_id": "system_prompt",
"label": {"type": "plain_text", "text":"システムプロンプト"},
"element": {
"type": "plain_text_input",
"action_id": "system_prompt",
"multiline":True,
"placeholder": {
"type": "plain_text",
"text": user_data["SystemPrompt"],
"emoji": True
}
}
}
]
}
view関数では、表示するモーダルの情報を生成しています。
モーダル送信時の処理
モーダル送信時には、以下の処理が実行されます。
import json
from slack_app import app
from lib.user_db import update_item
def edit_save(say, logger, body):
user_id = body["user"]["id"]
model_id = body["view"]["state"]["values"]["model_id"]["static_select-action"]["selected_option"]["value"]
system_prompt = body["view"]["state"]["values"]["system_prompt"]["system_prompt"]["value"]
update_item(user_id, model_id, system_prompt)
def ack_only(ack):
ack()
app.view("edit_view")(
ack=ack_only,
lazy=[edit_save]
)
上記の処理では、モーダルの入力値を受け取り、ユーザ情報の更新を行なっています。
モデル呼び出し関数
既存のchat.py内のモデル呼び出し関数(chat_use_history)を環境変数から引数で入力した値に変更します。
def chat_use_history(question, session_id, model_id, system_prompt):
chat = ChatBedrock(
model_id=model_id,
model_kwargs={"temperature": 0.1},
region_name=os.environ.get("ModelRegion")
)
prompt = ChatPromptTemplate.from_messages(
[
("system", system_prompt),
MessagesPlaceholder(variable_name="messages"),
]
)
chat_history = DynamoDBChatMessageHistory(
table_name=os.environ.get("TableName"),
primary_key_name=os.environ.get("KeyName"),
session_id=session_id,
)
# 質問追加
chat_history.add_user_message(question)
chain = prompt | chat
## chainの実行
result = chain.invoke(
{
"messages": chat_history.messages
}
)
# 回答結果の保存
chat_history.add_ai_message(result.content)
return result.content
動作確認
Slackにて「/edit」を入力すると、以下のようにモーダルが開きます。
モーダルへ変更したい情報を入力して、「送信」を押すと、変更内容の反映が行われます。
実際にモデルの呼び出しを行ってみると、確かにシステムプロンプト通りの回答ができています。
さいごに
サクッと生成AIを活用したい場合に、Slackのような既存のツールに寄せることで、ユーザインターフェースの作成は不要となりますが、カスタマイズをしようとなった時に少々大変です。
一方で、Slackでは、カスタマイズが簡単に行えるようなフレームワークもありますので気になった方はぜひ試してみてください。
参考